/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief ASRobo Compatible BUS baseclass
 * \author Antti Maula <antti.maula@tkk.fi>
 *
 * This is the implementation representing the whole Driver inheritance tree
 * used in ASRobo. Using this as a baseclass for ASRobo drivers simplifies
 * porting ASRobo Bus Driver implementation to MaCI.
 */
#ifndef _MACI_BUS_ASROBOBUS_HPP_
#define _MACI_BUS_ASROBOBUS_HPP_
#include "ownutils.h"
#include <string>


/** ASRobo Compatible BUS baseclass.
 * This is the implementation representing the whole Driver inheritance tree
 * used in ASRobo. Using this as a baseclass for ASRobo drivers simplifies
 * porting ASRobo Bus Driver implementation to MaCI.
 */
class CASRoboBus
{
public:

  /**
   * Returnvalues from ASRoboBus specific functions
   */
  enum EBusStatus { 
    KBusStatusOK               =  0, ///< All ok.
    KBusStatusError            = -1, ///< General error, no more information available
    KBusStatusTimeout          = -2, ///< Operation timed out
    KBusStatusUnimplemented    = -3, ///< Requested feature not implemented
    KBusStatusNotAvailable     = -4  ///< Operation not available right now.
  }; 

  /**
   * Mode selection bits supported by the ASRoboBus.
   */
  enum EBusModeBit { 
    KBusModeBitNonBlocking    =  (1<<0) ///< Select non-blocking operation.
  };
    

public:
  CASRoboBus();
  virtual ~CASRoboBus();
    
  /**
   * Opens the underlying Bus driver. Implementations should do all
   * necessary initializations etc. needed to figure out whether
   * the Bus is in usable state now. Modules should be pre-configured
   * prior to calling this function. (Like: Serial drivers should have
   * their assigned devices set etc)
   *
   * @return                The driver returns a value of OK or Error. 
   *                        (\see EBusStatus) If the Bus driver returns
   *                        an error value, the ASRobo server will
   *                        refuce to continue.
   */
  virtual EBusStatus Open(void) = 0;


  /**
   * This function matches closely the standard 'read()' function in C 
   * library. It reads from the assigned Bus, as specified in the 
   * particular busses manual. If the device is on Blocking mode,
   * the read function will not return until it has atleast one byte to return.
   * In non-blocking mode, it will return zero == no bytes available.
   * 
   * @param[out] aBuffer     Pointer to buffer to copy the read data.
   * @param[in] aBufferSize  Size of the buffer. (No more than this
   *                         can be copied)
   * @return                 Number of bytes read, or negative on error.
   *                         Zero bytes can be returned on non-blocking mode
   *                         when there is no data available.
   */
  virtual int Read(unsigned char *aBuffer, int aBufferSize) = 0;


  /**
   * This function matches closely the standard 'write()' function in
   * C library. It writes to assigned bus in a way more closely defined
   * in the particular Busses manual. If the device is on Blocking mode, 
   * the function will wait until all data
   * is transferred. If in NonBlocking mode, the function may return fewer bytes
   * written than requested (Also Zero is possible, in which case the output
   * buffer is full)
   *
   * @param[in] aBuffer     Pointer to buffer to read data from.
   * @param[in] aBufferSize Size of the buffer. (No more than this
   *                        can be copied)
   * @return                Number of bytes written, or negative on error.
   *                        Zero bytes can be returned on non-blocking mode
   *                        when there is no room for new data in output
   *                        buffers.
   *
   */
  virtual int Write(const unsigned char *aBuffer, int aBufferSize) = 0;


  /**
   * This functions closely matches the 'select()' or 'poll()' facility
   * of the standard C library. This function will block until 
   * a) ... timeout is reached
   * b) ... any number of input is available. (== Read() will not
   *    block even in Blocking mode)
   *
   * @param[in] aTimeout_ms Timeout in milliseconds to wait before
   *                        returning from call with no result. Value
   *                        of -1 will wait forever.
   * @return                KBusStatusOK when input is available,
   *                        KBusStatusTimeout on Timeout,
   *                        KBusStatusError on any error. (interrupted,
   *                        invalid handle, etc.)
   *
   */
  virtual EBusStatus WaitToRead(const int aTimeout_ms = -1) = 0;
      

  /**
   * This functions closely matches the 'select()' or 'poll()' facility
   * of the standard C library. This function will block until 
   * a) ... timeout is reached
   * b) ... there is space on output buffer (== Write() will not
   *    block even in Blocking mode)
   *
   * @param[in] aTimeout_ms Timeout in milliseconds to wait before
   *                        returning from call with no result. Value
   *                        of -1 will wait forever.
   * @return                KBusStatusOK when output is possible,
   *                        KBusStatusTimeout on Timeout,
   *                        KBusStatusError on any error. (interrupted,
   *                        invalid handle, etc.)
   *
   */
  virtual EBusStatus WaitToWrite(const int aTimeout_ms = -1) = 0;


  /**
   * This is a special Read function. It provides means to read a specific 
   * length buffer - with timeout. The function will attempt to read the FULL 
   * requested buffer or abort on timeout.
   * 
   * @param[out] aBuffer    Pointer to  buffer to copy data to
   * @param[in] aBlock_size Block size to read in bytes
   * @param[in] aTimeout_ms Timeout to wait in milliseconds.
   * @return                Number of bytes read. The value can
   *                        be smaller than requested in case of timeout.
   *                        In case of any other error, negative value
   *                        is returned.
   */
  virtual int ReadBlock(unsigned char *aBuffer, int aBlock_size, 
                        int aTimeout_ms = -1);


  /**
   * This is a special Read function. It provides means to read a specific 
   * length buffer - with timeout. The function will attempt to read the FULL 
   * requested buffer or abort on timeout.
   * 
   * @param[in] aBuffer     Pointer to  buffer to copy data from.
   * @param[in] aBlock_size Block size to read in bytes
   * @param[in] aTimeout_ms Timeout to wait in milliseconds.
   * @return                Number of bytes written. The value can
   *                        be smaller than requested in case of timeout.
   *                        In case of any other error, negative value
   *                        is returned.
   */
  virtual int WriteBlock(const unsigned char *aBuffer, int aBlock_size, 
                         int aTimeout_ms = -1);


  /**
   * This function will close the Bus driver, freeing all assigned resources.
   * However, this close is ofcourse instance specific, so for grouping
   * drivers; all other instances should remain unaffected.
   * 
   * @return                Bus Status of OK or Error is returned.
   */
  virtual EBusStatus Close(void) = 0;


  /**
   * This function sets one or multiple BusModeBits. The return value of
   * the function determines whether the operation succeeded. The default
   * implementation always accepts, but drivers may wish to override this
   * behaviour - for example to deny some mode bit change.
   *
   * @param[in] aBusModeBits Mask of bits to set.
   * @return                True if operation was succesfull,
   *                        false if failure. In case of failure,
   *                        NONE of the requested bits should have been
   *                        applied.
   */
  virtual bool SetBusMode(EBusModeBit aBusModeBits);


  /**
   * This function clears one or multiple BusModeBits. The return value of
   * the function determines whether the operation succeeded. The default
   * implementation always accepts, but drivers may wish to override this
   * behaviour - for example to deny some mode bit change.
   *
   * @param[in] aBusModeBits Mask of bits to clear.
   * @return                True if operation was succesfull,
   *                        false if failure. In case of failure,
   *                        NONE of the requested bits should have been
   *                        applied.
   */
  virtual bool ClearBusMode(EBusModeBit aBusModeBits);

protected:
  /**
   * Locks the driver device by Mutual Exclusion
   */
  void Lock(void);


  /**
   * Unlocks the driver device previously locked for mutual exclusion.
   */
  void Unlock(void);


  /**
   * Notify. Continues a thread which is blocking in 'Wait()'.
   */
  void Notify();

    
  /**
   * Wait for Notify().
   * @param[in] aTimeout_ms   Timeout in milliseconds to wait tops.
   *                          negative values to wait forever.
   * @return                  True if Notify received, false on
   *                          any error (including timeout)
   */
  bool Wait(const int aTimeout_ms = -1);

  /////////////////////////////////////////////////////////////////////////////////
  ownMutexHandle iMutexHandle; ///< Internal Mutex handle
  ownCondHandle iCondHandle; ///< Internal Condition handle
  unsigned int iBusMode;    ///< Current BusMode bits.
  bool iIsBusOpen;          ///< Is the Bus Open?
    
private:
  CASRoboBus(const CASRoboBus &) 
    : iMutexHandle(ownMutexHandleInitializer),
      iCondHandle(ownCondHandleNullInitializer),
      iBusMode(0),
      iIsBusOpen(false) {}
  CASRoboBus& operator= (const CASRoboBus &) { return *this; }
    
};
#endif //_DRIVER_HPP_
